/// <reference path="./Math.ts" />

namespace RAYTRACING {
    export class Camera {
        private static readonly helpRay: Ray = new Ray();
        private static readonly helpV3: Vector3 = new Vector3();
        private static readonly helpV3_1: Vector3 = new Vector3();
        private _node: Node;
        private _fov: number = 30;
        private _near: number = 0.1;
        /** 宽比高的值 */
        private _asp: number = 1;
        //
        //  Pos01  _____________  Pos11
        //         |           |
        //         |           |       near rect plane 
        //         |    .      |
        //         |   /       |
        //   Pos00 |__/________|  Pos10
        //           /  
        //          eye
        private _nearPos00: Vector3 = new Vector3();
        private _nearPos01: Vector3 = new Vector3();
        private _nearPos10: Vector3 = new Vector3();

        public get node() { return this._node; }
        public get fov() { return this._fov; }
        public set fov(val: number) {
            if (this._fov == val) return;
            this._fov = val;
            this.calcNearRect();
        }
        public get asp() { return this._asp; }
        public set asp(val) {
            if (this._asp == val) return;
            this._asp = val;
            this.calcNearRect();
        }
        public get near() { return this._near; }
        public set near(val) {
            if (this._near == val) return;
            this._near = val;
            this.calcNearRect();
        }

        constructor() {
            this._node = new Node();
            this.calcNearRect();
        }

        /**
         * 创建射线 通过 指定的uv
         * @param u near 矩形的u
         * @param v near 矩形的v
         * @param useShareRay 是否使用共享摄像（返回的射线是共享射线）
         * @returns 
         */
        public createRayByUV(u: number, v: number, useShareRay = true): Ray {
            let _ray: Ray = useShareRay ? Camera.helpRay : new Ray();
            let _n = this._node;

            //near 面上的点
            let nearPlanePos = Camera.helpV3;
            nearPlanePos.x = NumberLerp(this._nearPos00.x, this._nearPos10.x, u);
            nearPlanePos.y = NumberLerp(this._nearPos00.y, this._nearPos01.y, v);
            nearPlanePos.z = this._nearPos00.z;
            Quaternion.TransformVec3(_n.rotation, nearPlanePos, nearPlanePos);  //可优化为 node RT 改变时计算
            Vector3.Add(nearPlanePos, _n.position, nearPlanePos);               //可优化为 node RT 改变时计算
            //发射点
            _ray.origin.copy(nearPlanePos);
            //方向计算
            let dir = Camera.helpV3_1;
            Vector3.Sub(nearPlanePos, _n.position, dir);
            Vector3.Normalize(dir, _ray.direction);
            return _ray;
        }

        /** 计算 近面矩形 */
        private calcNearRect() {
            //通过 fov + near 求 rect 宽度
            let rhFov = this._fov * TO_RADIAN * 0.5;
            let c = this._near / Math.cos(rhFov);   //计算玄长
            let helf_W = c * Math.sin(rhFov);
            //通过 asp 求 高度
            let helf_H = helf_W / this._asp;

            //计算点
            this._nearPos00.set(-helf_W, -helf_H, this._near);
            this._nearPos10.set(helf_W, -helf_H, this._near);
            this._nearPos01.set(-helf_W, helf_H, this._near);
        }
    }
}